﻿using Gma.QrCodeNet.Encoding;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AnesthesiaSys.HardwareControlModule
{


    /// <summary>
    ///   命令  功能
    ///NUL  结束标志
    ///HT  执行水平造表
    ///LF  换行
    ///CR  回车
    ///ESC SP n  设置字间距
    ///ESC %   替换用户定义字符
    ///ESC &  用户自定义字符
    ///ESC ’   打印M个曲线点
    ///ESC + n  允许/标止上划线打印
    ///ESC – n  允许/标止下划线打印
    ///ESC 1 n  设定行间距为n点行
    ///ESC 6  选择字符集1 
    ///ESC 7  选择字符集2 
    ///ESC 8 n  选择不同点阵汉字打印
    ///ESC ：  恢复字符集中的字符
    ///ESC @  初始化打印机
    ///ESC D   设置水平造表值
    ///ESC E   打印条型码
    ///ESC J n  执行n点行走纸
    ///ESC K   打印n1×8点阵图形
    ///ESC Q n  设定右限宽度
    ///ESC U n  横向放大n倍
    ///ESC V n  纵向放大n倍 
    ///ESC W n  横向纵向放大n倍
    ///ESC X n1 n2  横向纵向放大不同倍数
    ///ESC C n  允许/禁止反向打印
    ///ESC f M n  打印空格或换行
    ///ESC i n  允许/禁止反白打印
    ///ESC l n  设定左限宽度
    ///ESC r 2B/2D n  热敏打印深度调整
    ///FS 2 n  设置字符旋转打印
    ///FS J  设置纵向打印
    ///FS K  设置横向打印
    ///FS r n  选择上下标
    ///FS 8  选择12X24ASCII 
    /// </summary>
    public class ESC_POS2
    {

        public ESC_POS2()
        {
            var sender = new ComPrinterDataSender();
            sender.Init();

            _sender = sender;

        }

        IPrinterDataSender _sender = null;

        public void Print(string str)
        {
            _sender.SendData2Printer(Encoding.GetEncoding("GBK").GetBytes(str));
        }

        /// <summary>
        /// ESC @ 
        ///[名称]  初始化打印机 
        ///[格式]  ASCII   ESC   @ 
        ///十进制   27  64 
        ///十六进制  1B  40 
        ///[说明]  清除打印缓冲区中的数据，复位打印参数到当前打印机缺省参数。
        ///[注意]  • 打印机接收缓冲区的数据并不被清除。
        /// </summary>
        public void Init()
        {
            byte[] data = new byte[] { 27, 64 };
            _sender.SendData2Printer(data);
        }

        public void Null()
        {
            byte[] data = new byte[] { 0 };
            _sender.SendData2Printer(data);
        }



        /// <summary>
        /// LF 
        ///[名称]  打印并换行
        ///[格式]  ASCII   LF 
        ///十进制   10 
        ///十六进制  0A 
        ///[说明]  把打印缓冲区中的数据打印出来，并换行。
        ///[注意]  • 该命令把打印位置设置为行的开始位置。
        /// </summary>
        public void NewLine()
        {
            byte[] data = new byte[] { 10 };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// CR 
        ///[名称]  打印并回车
        ///[格式]  ASCII   CR 
        ///十进制   13 
        ///十六进制  0D 
        ///[说明]  把打印缓冲区中的数据打印出来，并回车。
        ///[参考]  LF
        /// </summary>
        public void Return()
        {
            byte[] data = new byte[] { 13 };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC J 
        ///[名称]  打印并进纸
        ///[格式]  ASCII   ESC   J   n
        ///十进制   27   74   n
        ///十六进制 1B 4A  n
        ///[范围]  0 ≤ n ≤255
        ///[说明]  打印输出打印缓冲区中的数据，并进纸[n ⅹ0.125mm(0.0049”)]。 
        ///[注意]  • 打印结束后，将下一行的开始设定为打印起始位置。
        /// </summary>
        /// <param name="n"></param>
        public void FeedPaper(byte n)
        {
            byte[] data = new byte[] { 27, 74, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        ///        [名称]  打印空格或空行
        ///[格式] ASCII：ESC f M n 
        ///十进制： 27   102  M n 
        ///十六进制： 1B  66  M n 
        ///[说明] 
        ///如果M=0，ESC f NUL n命令将打印n个空格。每个空格的宽度按6+字间距（热敏按12+ 
        ///字间距）计算，n的值应在该型号打印机的行宽之内。
        ///如果M=1，ESC f SOH n命令将打印n行空行。每个空行的高度针打按8+行间距，热敏按
        ///16+行间距计算，n的值应在0~255之间。
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="n"></param>
        public void FeedPaper(bool mode, byte n)
        {
            byte[] data = new byte[] { 27, 100, (byte)(mode ? 1 : 0), n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC c 
        ///[名称]  允许/禁止反向打印
        ///[格式]  ASCII   ESC   c   n
        ///十进制   27   99   n
        ///十六进制 1B 63  n
        ///[范围]  0 ≤ n ≤1
        ///[描述]  当n=1 时，允许反向打印打印方向由左向右，当n=0 时，禁止反向打 
        ///印，打印方向由右向左。 
        ///[注意]  • 通常在打印机垂直安装时，会使用反向打印方式。反向打印不但支持字符方式，也支持图形方式。
        ///在反向打印图形时，请注意图形单元的打印顺序，参考 ESC K。 
        /// </summary>
        /// <param name="inverse"></param>
        public void Inverse(bool inverse)
        {
            byte[] data = new byte[] { 27, 99, (byte)(inverse ? 1 : 0) };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// HT 
        ///[名称]  水平制表 
        ///[格式]  ASCII   HT 
        ///十进制   9 
        ///十六进制  09 
        ///[说明]  移动打印位置到下一个水平制表位置。
        ///[注意]  • 通过ESC D命令设置水平制表位的位置。
        ///• 如果没有设置下一个水平制表位置，则该命令被忽略。 
        ///[参考]  ESC D。
        /// </summary>
        public void HorizonTab()
        {
            byte[] data = new byte[] { 9 };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC D n1 n2 … nk NULL 
        ///[名称]  设置水平制表位 
        ///[格式] ASCII码   ESC D n1…nk NULL 
        ///十进制码  27 68 n1…nk 0 
        ///十六进制码 1B 44 n1…nk 00 
        ///[范围] 1 ≤ n ≤255 0 ≤ k ≤20  
        ///[描述]  设置水平定位位置。 
        ///n 指定从一行开始的列号，用来设置水平定位位置。 
        ///k 表示将被设置水平定位点的总数。 
        ///[注意]  • 水平制表位置作为一个值储存，这个值为n个西文字符宽度，是从行的开始测量的。字符宽度包
        ///括字符间距的缺省字符宽。 
        ///• 该命令不受字符放大命令(ESC X)的影响。 
        ///• 该命令删除了之前设定的水平定位位置。 
        ///• 字符打印位置超过定位位置将被处理为普通数据。 
        ///• 按升序传输[n]k， 并且在末尾放置一个NULL码0。 
        ///• 该命令中nk>n(k-1),如果nk小于或等于前面的值n(k-1)，定位设定结束并且n(k-1)后面的数据按
        ///普通数据处理。 
        ///• ESC D NULL 取消所有水平定位位置。 
        ///• 即使字符宽度变化，以前指定的水平定位位置也不变。 
        ///[缺省值]  缺省定位位置为字体A (12 × 24)。 
        /// </summary>
        /// <param name="v"></param>
        public void SetHorizonTabTabs(byte[] tabs)
        {
            byte[] data = new byte[tabs.Length + 2];
            data[0] = 27;
            data[1] = 68;
            Array.Copy(tabs, 0, data, 2, tabs.Length);
            _sender.SendData2Printer(data);
        }

        /// <summary>
        ///ESC – n 
        ///[名称]  取消/设置下划线 
        ///[格式]  ASCII   ESC  -  n
        ///十进制   27  45 n
        ///十六进制 1B 2D  n
        ///[说明]  n= 1,允许下划线打印；n=0，取消下划线打印。
        ///[注意]  • 反白及旋转字符此命令无效。
        ///• 该命令仅对英文和汉字字符有效。 
        ///[缺省值]  n = 0  
        /// </summary>
        /// <param name="set"></param>
        public void SubLine(bool set)
        {
            byte[] data = new byte[] { 27, 45, (byte)(set ? 1 : 0) };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC + n 
        ///[名称]  取消/设置上划线 
        ///[格式]  ASCII   ESC  +  n
        ///十进制   27  43 n
        ///十六进制 1B 2B  n
        ///[说明]  n= 1,允许上划线打印；n=0，取消上划线打印。
        ///[注意]  • 反白及旋转字符此命令无效。
        ///• 该命令仅对英文和汉字字符有效。 
        ///[缺省值]  n = 0  
        /// </summary>
        /// <param name="set"></param>
        public void SupLine(bool set)
        {
            byte[] data = new byte[] { 27, 43, (byte)(set ? 1 : 0) };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// GS B n 
        ///[名称] 设置/取消反白打印 
        ///[格式] ASCII   GS  B  n
        ///十进制   29  66 n
        ///十六进制 1D 42  n
        ///[描述]  设置或取消反白打印。 
        ///当n 的最低有效位为0时，取消反白模式。 
        ///当n 的最低有效位为1时，设置反白模式。 
        ///[注意] • 仅n 的最低位有效。 
        ///• 该命令对内置字符和用户自定义字符均有效。 
        ///•该命令仅对英文和汉字字符有效。 
        ///[缺省值] n = 0  
        /// </summary>
        /// <param name="set"></param>
        public void Reverse(bool set)
        {
            byte[] data = new byte[] { 27, 105, (byte)(set ? 1 : 0) };
            _sender.SendData2Printer(data);
        }


        /// <summary>
        /// FS 2 n 
        ///[名称]  设置字符旋转打印 
        ///[格式] ASCII   FS  2  n
        ///十进制   28  73 n
        ///十六进制 1C 49  n
        ///[范围]  0 ≤ n ≤3  
        ///[描述]  设置字符旋转模式 
        ///N（十进制） 意义
        ///0  不旋转
        ///1  设置90逆时针旋转
        ///2  设置180逆时针旋转
        ///3  设置270逆时针旋转
        ///[注意]  •在90或270旋转模式下，字符放大命令的宽高放大方向与一般模式下的放大方向相反。 
        ///[缺省值] n = 0  
        /// </summary>
        public void SetAngle(byte n)
        {
            byte[] data = new byte[] { 28, 73, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// FS K 
        ///[名称]  设置横向打印
        ///[格式] ASCII： FS  K 
        ///十进制： 28  75 
        ///十六进制：1C 4B 
        ///[说明] 
        ///该命令设置字符为正向字符，即横向打印。打印方向是由左向右。
        /// </summary>
        public void SetHorizon()
        {

            byte[] data = new byte[] { 28, 75 };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// FS J 
        ///[名称]  设置纵向打印] 
        ///[格式] ASCII：FS J 
        ///十进制：28 74 
        ///十六进制：1C 4A 
        ///[说明] 
        ///该命令设置字符为左旋字符，即纵向打印。打印方向是由左向右。
        /// </summary>
        public void SetVertical()
        {

            byte[] data = new byte[] { 28, 74 };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC & 
        ///[名称]  定义用户自定义字符
        ///[格式] ASCII： ESC  &  m n1 n2…n6 
        ///十进制： 27   38  m n1 n2…n6 
        ///十六进制： 1B   26  m n1 n2…n6 
        ///[说明] 
        ///该命令允许用户定义一个字符。m是该用户定义字符码，应在32到255之间。n1，n2，…n6 
        ///是定义这个字符的结构码。字符是由6×8点阵组成。共6列每列8点，每一列由一个字节的
        ///数据表示，最高位在上。
        ///n1=02H，n2=7CH，n3=40H，n4=COH，n5=40H，n6=0H 
        ///用户定义的字符储存在RAM内，断电后将会丢失。
        ///如果许多ESC U命令使用同一m值，只有最后一个有效。最多可以定义32个用户定义字符
        /// </summary>
        /// <param name="m"></param>
        /// <param name="n6"></param>
        public void DefineChar(byte m, byte[] n6)
        {
            byte[] data = new byte[] { 27, 38, m, 0, 0, 0, 0, 0, 0 };
            Array.Copy(n6, 0, data, 3, 6);
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC % 
        ///[名称]  替换用户定义字符
        ///[格式] ASCII： ESC % m1 n1 m2 n2…mk nk NUL 
        ///十进制： 27 37 m1 n1 m 2 n2…mk nk 0 
        ///十六进制：1B 25 m1 n1 m2 n2…mk nk 0 
        ///[说明] 
        ///该命令可以将当前字符集中的字符n替换为用户定义字符m。在该命令以后的用户定义字符m
        ///将会代替当前字符集中的字符n打印出来。
        ///m1，m2…mk是用户定义的字符码
        ///n1，n2…nk是当前字符集中要被替换的字符码
        ///m和n的数值都应在32到255之间. 
        ///下标K的值应在1到32之间。最多可替换的字符数是32。
        ///字符NUL加在最后表示该命令的结束。
        /// </summary>
        /// <param name="map"></param>
        public void ReplaceChar(byte[] map)
        {
            byte[] data = new byte[map.Length + 3];
            data[0] = 27;
            data[1] = 37;
            data[data.Length - 1] = 0;
            Array.Copy(map, 0, data, 2, map.Length);
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC  ： 
        ///[名称]  恢复字符集中的字符
        ///[格式] ASCII：ESC  ：
        ///十进制：27 58 
        ///十六进制：1B 3A 
        ///[说明] 
        ///该命令恢复字符集中的原字符，该字符在此之前已被用ESC %命令替换为用户字义字符。但
        ///是用户定义字符并不从RAM中删除，它还可以用ESC%命令调出使用
        /// </summary>
        public void ResetCharset()
        {
            byte[] data = new byte[] { 27, 58 };
            _sender.SendData2Printer(data);
        }



        /// <summary>
        /// ESC l n 
        ///[名称]  设置左侧不打印区域 
        ///[格式] ASCII   ESC  l  n
        ///十进制   27  108 n
        ///十六进制 1B 6C  n
        ///[范围]  0 ≤  n  ≤32  
        ///[描述]  设定左侧不打印的字符数。 
        ///从一行的开始到打印位置的距离为n个西文字符宽度。 
        ///[注意]  • 如果设定的打印位置超出了可打印区域，命令将被忽略。 
        ///• 字符宽度包括字符间距的缺省字符宽。 
        /// </summary>
        /// <param name="n"></param> 
        public void SetLeftBound(byte n)
        {
            byte[] data = new byte[] { 27, 108, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC Q n 
        ///[名称]  设置右侧不打印区域 
        ///[格式] ASCII   ESC  Q  n
        ///十进制   27  81 n
        ///十六进制 1B 51  n
        ///[范围]  0 ≤  n  ≤32  
        ///[描述]  设定右侧不打印的字符数。 
        ///[注意]  • 如果设定的打印位置超出了可打印区域，命令将被忽略。 
        ///• 字符宽度包括字符间距的缺省字符宽。 
        /// </summary>
        /// <param name="n"></param>
        public void SetRightBound(byte n)
        {
            byte[] data = new byte[] { 27, 81, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC 1 n 
        ///[名称]  设置行间距 
        ///[格式] ASCII   ESC  1  n
        ///十进制   27  49 n
        ///十六进制 1B 31  n
        ///[范围]  0 ≤ n ≤255  
        ///[描述]  设置字符行间距为n个垂直点距。 
        ///[缺省值]  n = 3  
        /// </summary>
        /// <param name="n"></param>
        public void SetLineGap(byte n)
        {
            byte[] data = new byte[] { 27, 49, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC SP n 
        ///[名称]  设置字间距 
        ///[格式] ASCII   ESC  SP n
        ///十进制   27  32 n
        ///十六进制 1B 20  n
        ///[范围]  0 ≤ n ≤255  
        ///[描述]  设置字符字间距为n个水平点距。 
        ///[缺省值]  n = 0  
        /// </summary>
        /// <param name="n"></param>
        public void SetWordGap(byte n)
        {
            byte[] data = new byte[] { 27, 32, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC a n 
        ///[名称]  选择对齐方式 
        ///[格式] ASCII   ESC  a  n
        ///十进制   27  97 n
        ///十六进制 1B 61  n
        ///[范围] 0 ≤ n ≤2  
        ///[描述]  将一行数据按照n指定的位置对齐。 
        ///N   意义 
        ///0  左对齐 
        ///1  居中 
        ///2  右对齐 
        ///[注意] •仅在一行的开始处理时，该命令才有效。 
        ///[缺省值] n = 0  
        /// </summary>
        /// <param name="n"></param>
        [Obsolete]
        public void SetHorizontalAlignment(byte n)
        {
            byte[] data = new byte[] { 27, 97, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// FS r n 
        ///[名称]  选择上下标 
        ///[格式] ASCII   FS  r  n
        ///十进制   28  114 n
        ///十六进制 1C 72  n
        ///[范围] 0 ≤ n ≤1  
        ///[描述]  n=0 选择上标，一行字符图形顶部对齐。
        ///n=1 选择下标，一行字符图形低部对齐  
        ///[注意] • 该命令对所有字符(英数字符和汉字) 有效。 
        ///• 如果n 在定义范围之外，忽略该命令。 
        /// </summary>
        /// <param name="sub"></param>
        public void SelectSubSup(bool sub)
        {
            byte[] data = new byte[] { 28, 114, (byte)(sub ? 1 : 0) };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC U n 
        ///[名称]  水平放大字符 
        ///[格式] ASCII   ESC  U  n
        ///十进制   27  85 n
        ///十六进制 1B 55  n
        ///[范围] 1 ≤ n ≤8  
        ///[注意] • 该命令对所有字符(英数字符和汉字) 有效。 
        ///• 如果n 在定义范围之外，忽略该命令。 
        ///[参考] ESC X
        /// </summary>
        /// <param name="zoom"></param>
        public void SetHorizonalZoom(byte zoom)
        {
            byte[] data = new byte[] { 27, 85, zoom };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC V n 
        ///[名称]  垂直放大字符 
        ///[格式] ASCII   ESC  V  n
        ///十进制   27  86 n
        ///十六进制 1B 56  n
        ///[范围] 1 ≤ n ≤8  
        ///[注意] • 该命令对所有字符(英数字符和汉字) 有效。 
        ///• 如果n 在定义范围之外，忽略该命令。  
        ///[参考] ESC X
        /// </summary>
        /// <param name="zoom"></param>
        public void SetVerticalZoom(byte zoom)
        {
            byte[] data = new byte[] { 27, 86, zoom };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC W 
        ///[名称]  横向纵向放大
        ///[格式] ASCII：ESC W n 
        ///十进制： 27 87 n 
        ///十六进制：1B 57 n 
        ///[说明] 
        ///在该命令之后的字符将以正常宽度和高度的n倍打印。针打ｎ的值在1～8之间，默认值：n=1，
        ///即无横向纵向放大。热敏ｎ的值在0～8之间，默认值：n=1，即横向纵向放大1倍。
        ///[例子] 
        /// </summary>
        /// <param name="zoom"></param> 
        public void SetZoom(byte zoom)
        {
            byte[] data = new byte[] { 27, 87, zoom };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC X 
        ///[名称]  横向纵向放大不同倍数
        ///[格式] ASCII：ESC X n1 n2 
        ///十进制： 27  88 n1 n2 
        ///十六进制：1B  58 n1 n2 
        ///[说明] 
        ///在该命令之后的字符将以正常宽度的n1倍和正常高度的n2倍进行打印。n1，n2的值在1到8
        ///之间。默认值n1=n2=1，即无放大。
        /// </summary>
        /// <param name="zh"></param>
        /// <param name="zv"></param>
        public void SetZoom(byte zh, byte zv)
        {
            byte[] data = new byte[] { 27, 88, zh, zv };
            _sender.SendData2Printer(data);
        }


        /// <summary>
        /// ESC r 
        ///[名称]  热敏打印深度调整
        ///[格式] ASCII：ESC r 2B/2D n 
        ///十进制：27 114 43/45 n 
        ///十六进制：1B  72  2B/2D n 
        ///[说明] 
        ///n的值在０～15之间；
        ///当使用1B 72 2B n 为加强深度调节，n值越大打印深度越深。
        ///当使用1B 72 2D n 为减少深度调节，n值越大打印深度越浅。
        ///恢复指令为1B 72 2D 0或1B 72 2B 0 
        ///[注意] 
        ///用户可根据实际情况进行打印深度的调节，当调浅时，会降低打印机的功耗，增加深度时，打
        ///印机功耗会增加。
        /// </summary>
        /// <param name="n"></param>
        public void SetInkDeep(bool plus, byte n)
        {
            byte[] data = new byte[] { 27, 114, (byte)(plus ? 43 : 45), n };
            _sender.SendData2Printer(data);
        }


        /// <summary>
        ///        ESC K 
        ///[名称]  打印点阵图形
        ///[格式] ASCII：ESC K n1 n2 …data…   十进制：27  75 n1 n2 …data… 
        ///十六进制： 1B   4B n1 n2 …data… 
        ///[说明] 
        ///该命令打印n1×8点阵图形。该图形的宽度为n1点，高度为8点。每一列的8个点可以由一个
        ///8位的字节来表示，最高位在上。
        ///n1，n2的数值代表一个16位的二进制数，n1为低8位字节，n2为高8位字节，表示ESC K 
        ///命令要打印的图形宽度为n2×256+n1。n2应是0或1，点数之和小于等于384，若超出则自动
        ///清除。n1应在1到该机型的每行最大点数之间。
        ///Data是该点阵图形从左到右每列的字节内容，字节数应等于n1。
        ///当图形的高度大于8点时，可按每8点行一个图形单元划分成多个单元。不足8点的用空点补
        ///齐。然后按顺序每个图形单元分别用ESC K命令打印出来，最后组成一个完整的图形。
        ///[注意] 
        ///当采用反向打印方式时，要按图形从下到上的顺序依次打印每个图形单元。
        /// </summary>
        /// <param name="width"></param>
        /// <param name="bitmap"></param>
        public void PrintBitmap(ushort width, byte[] bitmap)
        {
            byte[] data = new byte[bitmap.Length + 4];
            data[0] = 27;
            data[1] = 75;
            data[2] = (byte)width;
            data[3] = (byte)(width >> 8);
            Array.Copy(bitmap, 0, data, 4, bitmap.Length);
            _sender.SendData2Printer(data);
        }

        public void PrintBitmap(Bitmap bitmap)
        {
            if (bitmap.Width > 384) { throw new Exception("Width>384"); }
            ushort width = (ushort)bitmap.Width;

            for (int i = 0; i < bitmap.Height; i += 8)
            {
                byte[] bts = new byte[width];
                for (int j = 0; j < bitmap.Width; j++)
                {

                    for (int k = 0; k < 8; k++)
                    {
                        Color c = bitmap.GetPixel(j, k + i);
                        if (c.GetBrightness() < 0.3)
                        {
                            bts[j] = (byte)(bts[j] | (1 << k));
                        }
                    }

                }
                Return();
                PrintBitmap(width, bts);
            }
        }

        public void Print2DCode(int maxWidth, string content)
        {
            QrCode code = null;
            new QrEncoder(ErrorCorrectionLevel.H).TryEncode(content, out code);
            int pointWidth = maxWidth / code.Matrix.Width;

            var bars = TranslateFromMatrix(code.Matrix, pointWidth);

            var height = (code.Matrix.Width * pointWidth) / 8 +
                ((code.Matrix.Width * pointWidth) % 8 == 0 ? 0 : 1);
            var width = code.Matrix.Width * pointWidth;

            for (int r = 0; r < height; r++)
            {
                byte[] bts = new byte[width];
                for (int c = 0; c < width; c++)
                {
                    bts[c] = 0;

                    for (int i = 0; i < 8; i++)
                    {
                        if (r * 8 + i < width)
                        {
                            bts[c] = (byte)(bts[c] + ((bars[r * 8 + i, c] ? 1 : 0) << i));
                        }

                    }
                }

                PrintBitmap((ushort)(width), bts);
                Return();
            }

        }

        private bool[,] TranslateFromMatrix(BitMatrix matrix, int width)
        {
            bool[,] ret = new bool[matrix.Width * width, matrix.Width * width];
            for (int c = 0; c < matrix.Width; c++)
            {
                for (int r = 0; r < matrix.Height; r++)
                {
                    for (int cc = 0; cc < width; cc++)
                    {
                        for (int rr = 0; rr < width; rr++)
                        {
                            ret[c * width + cc, r * width + rr] = matrix[c, r];
                        }
                    }
                }
            }
            return ret;
        }


        public void Print2DCode(string content)
        {
            QrCode code = null;
            new QrEncoder(ErrorCorrectionLevel.H).TryEncode(content, out code);
            byte[,] bts = new byte[code.Matrix.Width, code.Matrix.Width * 8];

            for (int i = 0; i < code.Matrix.Height; i += 1)
            {
                for (int j = 0; j < code.Matrix.Width; j++)
                {
                    // if (i < 8 || i >= 13 || j < 8 || j >= 13)
                    {
                        byte b = (byte)(code.Matrix[j, i] ? 255 : 0);
                        for (int k = j * 8; k < j * 8 + 8; k++)
                        {
                            bts[i, k] = b;
                        }
                    }
                }
            }

            byte[] bs = new byte[code.Matrix.Width * 8];
            for (int i = code.Matrix.Width - 1; i >= 0; i--)
            {
                for (int j = 0; j < code.Matrix.Width * 8; j++)
                {
                    bs[j] = bts[i, j];
                }
                PrintBitmap((ushort)(code.Matrix.Width * 8), bs);
                Return();
            }

        }

        /// <summary>
        ///       ESC E 
        ///[名称]  打印条型码
        ///[格式] ASCII： ESC  E  nq nc n1 n2 n3…nk NUL 
        ///十进制： 27   69  nq nc n1 n2 n3…nk 0 
        ///十六进制： 1B   45  nq nc n1 n2 n3…nk 00 
        ///[说明] 
        ///nq：条码第1条线离打印纸端的距离（点）。N的值应在0到64之间。
        ///nc：条码线纵向高度（点）。n的值应在1到255之间。
        ///nk：第k个条码线的参数：
        ///高4位（D7～D4）与前一个条码线之间的空白点数。n的值应在1到15之间。
        ///低4位（D3～D0）该条码线的宽度（点）。K为条码线数量，k的值应在1到15之间。
        ///[注意] 
        ///本命令只适用热敏系列。
        /// </summary>
        /// <param name="bardata"></param>
        /// <param name="nq"></param>
        /// <param name="nc"></param>
        public void PrintBarCode(byte nq, byte nc, byte[] bardata)
        {

            var bar = bardata.Select(a => (byte)a).ToArray();
            byte[] data = new byte[bar.Length + 5];
            data[0] = 27;
            data[1] = 69;
            data[2] = nq;
            data[3] = nc;
            data[data.Length - 1] = 0;
            Array.Copy(bar, 0, data, 4, bar.Length);
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC ‘ 
        ///[名称]  打印一水平行上n个点 
        ///[格式]  ASCII 码  GS  ‘  nL  nH  x1L x1H x21L x21H ……. xkL xkH CR
        ///十进制码  29  39  nL  nH  x1L x1H x21L x21H ……. xkL xkH 13
        ///十六进制码  1B  27  nL nH x1L x1H x21L x21H ……. xkL xkH 0D 
        ///[范围] 0 ≤ nL ≤255 
        ///  0 ≤ nHL ≤1 
        ///  曲线点数N = nH x 256 + nL 
        ///  曲线点在水平行上的位置 X =xkH x 256 +xkL 。 
        ///[描述]  每条曲线都是由很多点组成。本指令为打印一水平行上 n 个点，连续使用该指令可以打印出用户所
        ///需要的曲线。
        /// </summary>
        /// <param name="n"></param>
        /// <param name="points"></param>
        public void PrintCurve(ushort n, byte[] points)
        {
            byte[] data = new byte[points.Length + 5];
            data[0] = 29;
            data[1] = 39;
            data[2] = (byte)n;
            data[3] = (byte)(n >> 8);
            data[data.Length - 1] = 13;
            Array.Copy(points, 0, data, 4, points.Length);
            _sender.SendData2Printer(data);
        }


        /// <summary>
        ///    [名称]  打印曲线Ⅰ
        ///[格式] ASCII： ESC  ’  m n1 n2…nk CR 
        ///十进制： 27   39 m n1 n2 …nk 13 
        ///十六进制： 1B   27 m n1 n2 …nk 0D 
        ///[说明] 
        ///该命令用于沿走纸方向打印曲线图形.。m的数值是要打印的曲线条数。它应当在1到该机型
        ///每行最大点数之间。
        ///在一水平点行内，有m个曲线点.。n1 ,n2 ,…nk代表这m个曲线的位置。nk的数量应等于m。
        ///每一nk都应小于该微打型号每行最大点数。最后的CR（回车）是让打印机打印出这一点行。
        ///整个m条曲线的图形就是由每一点行用n1，n2，…nk这些数据打印出来的。
        ///连续使用本命令可打印出任意长度的曲线。
        ///[注意] 
        ///本命令只适用于针打及部分热敏机型。
        /// </summary>
        /// <param name="n"></param>
        /// <param name="points"></param>
        public void PrintCurve(byte n, byte[] points)
        {
            byte[] data = new byte[points.Length + 4];
            data[0] = 29;
            data[1] = 39;
            data[2] = n;

            data[data.Length - 1] = 13;
            Array.Copy(points, 0, data, 3, points.Length);
            _sender.SendData2Printer(data);
        }


        /// <summary>
        ///  ESC 8 n 
        ///[名称]  选择不同点阵汉字打印
        ///[格式] ASCII：ESC  8 n 
        ///十进制：27 56 n 
        ///十六进制1B 38 n 
        ///[范围] 0 ≤n ≤7 
        ///[说明] 
        ///打印机在接收该命令之后将根据n值选择不同点阵的汉字。在汉字打印方式中，打印机接收的
        ///汉字代码是2字节对应一个汉字的标准机内码，即打印机每接收2个字节的机内码可调出一个
        ///汉字。打印机先接收机内码的高位字节，再接收低位字节。
        ///当n=0选择16×16点阵汉字打印
        ///当n=1选择8×16点阵汉字打印
        ///当n=2选择16×8点阵汉字打印
        ///当n=3选择8×8点阵汉字打印
        ///当n=4选择12×12点阵汉字印
        ///当n=5选择6×12点阵汉字打印
        ///当n=6选择8×16点阵ASCII字符打印
        ///当n=7选择8×12点阵ASCII字符打印
        ///汉字代码为标准汉字内码，可通过网上下载汉字内码表来查找汉字内码：
        ///1）高字节数值范围A1H-F7H，对应1-87区汉字，计算方法：区码+A0H 
        ///2）低字节数值范围A1H-FEH，对应汉字位码1-94，计算方法：位码+A0H 
        ///例：“荣”字的区位码是4057，即40区，第57个字，其机内码为C8D9 
        ///将40（十进制）转为28（十六进制），28H+A0H =C8H  
        ///将57（十进制）转为39（十六进制），39H+A0H=D9H 
        ///当输入代码为20H-A0H时，自动选择字符集（见附录1）
        ///当输入代码大于A0H时，如果下一字节小于A1H，则选择国际标准ASCII码，否则打印汉字。
        ///[注意] 
        ///1）24 点阵汉字库的机型n无1～7选项，字符为12×24点阵ASCII码，汉字命令为：1B 38 或
        ///1B 38 0 
        ///2）16 点阵汉字库的机型n的默认值为0。 
        /// </summary>
        public void SetCharMode(byte n)
        {
            byte[] data = new byte[] { 27, 56, n };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// FS. 
        ///[名称]  取消汉字模式 
        ///[格式] ASCII   FS  . 
        ///十进制   28  46 
        ///十六进制  1C  2E 
        ///[描述]： 取消汉字字符模式，当取消汉字字符模式后，超过0x80的编码仍然当作ASCII字符处理，将不再打
        ///印汉字，除非再用FS &命令选择汉字模式。打印机进入汉字打印模式。
        /// </summary>
        public void UnSetCharMode()
        {
            byte[] data = new byte[] { 28, 46 };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// ESC 6 
        ///[名称]  选择6X8字符集1
        ///[格式] ASCII   ESC  6 
        ///十进制   27  54 
        ///十六进制  1B  36 
        ///[描述]： 在该命令输入之后的所有字符均使用字符集1中的字符打印(见附录D)字符集1中有6×8点阵
        ///字符224个，包括ASCII字符及各种图形符号等。代码范围20H~FFH(32~255)。
        /// </summary>
        public void SelectCharSet1()
        {
            byte[] data = new byte[] { 27, 54 };
            _sender.SendData2Printer(data);
             
        }



        /// <summary>
        /// ESC 7 
        ///[名称]  选择6X8字符集2
        ///[格式] ASCII   ESC  7 
        ///十进制   27  55 
        ///十六进制  1B  37 
        ///[描述]  在该命令输入之后的所有字符均使用字符集2中的字符打印（见符录D），字符集2中有6×8点
        ///阵字符224个，包括德、法、俄文、日语片假名等。代码范围20H~FFH(32~255)。
        /// </summary>
        public void SelectCharSet2()
        {
            byte[] data = new byte[] { 27, 55 };
            _sender.SendData2Printer(data);
        }

        /// <summary>
        /// FS 8 
        ///[名称]  选择12*24 ASCII集
        ///[格式] ASCII： FS  8 
        ///十进制： 28  56 
        ///十六进制： 1C  38 
        ///[说明] 
        ///该命令仅适用于24点阵热敏机型，输入该命令之后将打印12X24点阵ASCII集字符（见符录
        ///1），代码范围20H~FFH(32~255)。
        /// </summary>
        public void SelectASC2_12_24()
        {
            byte[] data = new byte[] { 28, 56 };
            _sender.SendData2Printer(data);
        }
    }
}
